package redis

import "errors"

type ZData struct {
	Score  int64
	Member any
}

func (k *key) ZAdd(data ...*ZData) IntResult {
	args := []any{k.key}
	for _, v := range data {
		args = append(args, v.Score, v.Member)
	}
	return k.cmd("ZADD", args...)
}

func (k *key) ZScore(member any) IntResult {
	return k.cmd("ZSCORE", k.key, member)
}

func (k *key) ZIncrBy(data *ZData) IntResult {
	return k.cmd("ZINCRBY", k.key, data.Score, data.Member)
}

func (k *key) ZCard() IntResult {
	return k.cmd("ZCARD", k.key)
}

func (k *key) ZCount(min, max int64) IntResult {
	return k.cmd("ZCOUNT", k.key, min, max)
}

func (k *key) ZRange(start, stop int) ListResult {
	return k.cmd("ZRANGE", k.key, start, stop)
}

func (k *key) ZRangeWithScores(start, stop int) ZRangeResult {
	return k.cmd("ZRANGE", k.key, start, stop, "WITHSCORES")
}

func (k *key) ZRevRange(start, stop int) ListResult {
	return k.cmd("ZREVRANGE", k.key, start, stop)
}

func (k *key) ZRevRangeWithScores(start, stop int) ZRangeResult {
	return k.cmd("ZREVRANGE", k.key, start, stop, "WITHSCORES")
}

func (k *key) ZRangeByScore(min, max int64) ListResult {
	return k.cmd("ZRANGEBYSCORE", k.key, min, max)
}

func (k *key) ZRangeByScoreWithScores(min, max int64) ZRangeResult {
	return k.cmd("ZRANGEBYSCORE", k.key, min, max, "WITHSCORES")
}

func (k *key) ZRangeByScoreWithLimit(min, max int64, limitOffset, limitCount int) ListResult {
	return k.cmd("ZRANGEBYSCORE", k.key, min, max, "LIMIT", limitOffset, limitCount)
}

func (k *key) ZRangeByScoreWithScoresLimit(min, max int64, limitOffset, limitCount int) ZRangeResult {
	return k.cmd("ZRANGEBYSCORE", k.key, min, max, "WITHSCORES", "LIMIT", limitOffset, limitCount)
}

func (k *key) ZRevRangeByScore(min, max int64) ListResult {
	return k.cmd("ZREVRANGEBYSCORE", k.key, min, max)
}

func (k *key) ZRevRangeByScoreWithScores(min, max int64) ZRangeResult {
	return k.cmd("ZREVRANGEBYSCORE", k.key, min, max, "WITHSCORES")
}

func (k *key) ZRevRangeByScoreWithLimit(min, max int64, limitOffset, limitCount int) ListResult {
	return k.cmd("ZREVRANGEBYSCORE", k.key, min, max, "LIMIT", limitOffset, limitCount)
}

func (k *key) ZRevRangeByScoreWithScoresLimit(min, max int64, limitOffset, limitCount int) ZRangeResult {
	return k.cmd("ZREVRANGEBYSCORE", k.key, min, max, "WITHSCORES", "LIMIT", limitOffset, limitCount)
}

func (k *key) ZRank(member any) IntResult {
	return k.cmd("ZRANK", k.key, member)
}

func (k *key) ZRevRank(member any) IntResult {
	return k.cmd("ZREVRANK", k.key, member)
}

func (k *key) ZRem(member ...any) IntResult {
	return k.cmd("ZREM", append([]any{k.key}, member...)...)
}

func (k *key) ZRemRangeByRank(start, stop int) IntResult {
	return k.cmd("ZREMRANGEBYRANK", k.key, start, stop)
}

func (k *key) ZRemRangeByScore(min, max int64) IntResult {
	return k.cmd("ZREMRANGEBYSCORE", k.key, min, max)
}

func (k *key) ZRangeByLex(min, max int64) ListResult {
	return k.cmd("ZRANGEBYLEX", k.key, min, max)
}

func (k *key) ZRangeByLexWithLimit(min, max int64, limitOffset, limitCount int) ListResult {
	return k.cmd("ZRANGEBYLEX", k.key, min, max, "LIMIT", limitOffset, limitCount)
}

func (k *key) ZLexCount(min, max int64) IntResult {
	return k.cmd("ZLEXCOUNT", k.key, min, max)
}

func (k *key) ZRemRangeByLex(min, max int64) IntResult {
	return k.cmd("ZREMRANGEBYLEX", k.key, min, max)
}

type ZAggArgs struct {
	Weights   []int
	Aggregate string // SUM|MIN|MAX
}

func (a *ZAggArgs) args(keys []string) ([]any, error) {
	var args []any
	if a.Weights != nil && len(a.Weights) > 0 {
		if len(a.Weights) != len(keys) {
			return nil, errors.New("ZAggArgs.Weights has different count with keys")
		}
		args = append(args, "WEIGHTS")
		for _, w := range a.Weights {
			args = append(args, w)
		}
	}
	if a.Aggregate != "" {
		if a.Aggregate != "SUM" && a.Aggregate != "MIN" && a.Aggregate != "MAX" {
			return nil, errors.New("ZAggArgs.Aggregate expects SUM|MIN|MAX")
		}
		args = append(args, "AGGREGATE", a.Aggregate)
	}
	return args, nil
}

func (k *key) ZUnionStore(args *ZAggArgs) IntResult {
	if len(k.keys) < 2 {
		return cmdErr(errors.New("must more than 2 keys"))
	}
	keys := k.keys[1:]
	aa := []any{k.key, len(keys)}
	for _, k := range keys {
		aa = append(aa, k)
	}
	if args != nil {
		ab, err := args.args(keys)
		if err != nil {
			return cmdErr(err)
		}
		aa = append(aa, ab...)
	}
	return k.cmd("ZUNIONSTORE", aa...)
}

func (k *key) ZInterStore(args *ZAggArgs) IntResult {
	if len(k.keys) < 2 {
		return cmdErr(errors.New("must more than 2 keys"))
	}
	keys := k.keys[1:]
	aa := []any{k.key, len(keys)}
	for _, k := range keys {
		aa = append(aa, k)
	}
	if args != nil {
		ab, err := args.args(keys)
		if err != nil {
			return cmdErr(err)
		}
		aa = append(aa, ab...)
	}
	return k.cmd("ZINTERSTORE", aa...)
}

func (k *key) ZDiffStore() IntResult {
	if len(k.keys) < 2 {
		return cmdErr(errors.New("must more than 2 keys"))
	}
	keys := k.keys[1:]
	aa := []any{k.key, len(keys)}
	for _, k := range keys {
		aa = append(aa, k)
	}
	return k.cmd("ZDIFFSTORE", aa...)
}

func (k *key) ZScan(args *ScanArgs) error {
	return k.scan("ZSCAN", args)
}

func (k *key) ZPopMin(count int) ZRangeResult {
	if count < 1 {
		count = 1
	}
	return k.cmd("ZPOPMIN", k.key, count)
}

func (k *key) ZPopMax(count int) ZRangeResult {
	if count < 1 {
		count = 1
	}
	return k.cmd("ZPOPMAX", k.key, count)
}

func (k *key) BZPopMin(timeout int) ListResult {
	var args []any
	for _, k := range k.keys {
		args = append(args, k)
	}
	args = append(args, timeout)
	return k.cmd("BZPOPMIN", args...)
}

func (k *key) BZPopMax(timeout int) ListResult {
	var args []any
	for _, k := range k.keys {
		args = append(args, k)
	}
	args = append(args, timeout)
	return k.cmd("BZPOPMAX", args...)
}
